﻿using System;

namespace CN2P.ServiceModel.ServiceClients
{
    using System.Collections.Generic;
    using System.Net;

    public class ProductsServiceClient : BaseServiceClient
    {
        public ProductsServiceClient(string baseUrl)
            : base(baseUrl)
        {
        }

        public string CreateProduct<T>(T product) where T : FullProductModel
        {
            return this.CreateProduct(this.Serialize(product.GetProps()));
        }

        public string CreateProduct(string productJson)
        {
            var url = GetProductsUrl();
            var response = this.ExecutePost(url, productJson);
            this.ValidateResponse(response, HttpStatusCode.Created);
            return response.GetResultId();
        }

        public void UpdateProduct(string prodId, FullProductModel product)
        {
            this.UpdateProduct(prodId, this.Serialize(product.GetProps()));
        }

        public void UpdateProduct(string prodId, string productJson)
        {
            var url = this.GetProductUrl(prodId);
            var response = this.ExecutePut(url, productJson);
            this.ValidateResponse(response, HttpStatusCode.Created);
        }

        public void DeleteAll()
        {
            var url = this.GetProductsUrl() + "delete";
            var response = ExecuteDelete(url);
            this.ValidateResponse(response, HttpStatusCode.OK);
        }

        public IList<string> GetTags()
        {
            var url = this.GetProductsUrl() + "tags";
            var response = this.ExecuteGet<IList<string>>(url);
            this.ValidateResponse(response, HttpStatusCode.OK);

            return response.Content;
        }

        public ProductModelPagedList Get(int pageNumber = 1, int pageSize = 10)
        {
            var url = this.GetProductsUrl() + "?pageIndex=" + pageNumber + "&pageSize=" + pageSize;
            var response = this.ExecuteGet<ProductModelPagedList>(url);
            this.ValidateResponse(response, HttpStatusCode.OK);

            return response.Content;
        }

        public ProductModelPagedList GetByTag(string tag, int pageNumber = 0, int pageSize = 10)
        {
            var url = this.GetProductsUrl() + "search/tag?key=" + tag + "&pageIndex=" + pageNumber + "&pageSize=" + pageSize;
            var response = this.ExecuteGet<ProductModelPagedList>(url);
            this.ValidateResponse(response, HttpStatusCode.OK);

            return response.Content;
        }

        public ProductModelPagedList GetByName(string name, int pageNumber = 0, int pageSize = 10)
        {
            var url = GetProductsUrl() + "search/name?key=" + name + "&pageIndex=" + pageNumber + "&pageSize=" + pageSize;
            var response = this.ExecuteGet<ProductModelPagedList>(url);
            this.ValidateResponse(response, HttpStatusCode.OK);

            return response.Content;
        }

        public ProductModelPagedList GetByIds(string[] ids, int pageNumber = 0, int pageSize = 10)
        {
            string keys = string.Empty;
            foreach (var key in ids)
            {
                if (!string.IsNullOrEmpty(keys))
                    keys += "&";

                keys += "key=" + key;
            }

            var url = GetProductsUrl() + "search/prodid?" + keys;
            var response = this.ExecuteGet<ProductModelPagedList>(url);
            this.ValidateResponse(response, HttpStatusCode.OK);

            return response.Content;
        }

        public ProductModel GetProduct(string prodId)
        {
            var url = GetProductUrl(prodId);
            var response = this.ExecuteGet<ProductModel>(url);
            this.ValidateResponse(response, HttpStatusCode.OK);

            return response.Content;
        }

        public FullProductModel GetProductDetails(string prodId)
        {
            return this.GetProductDetails(prodId, props => new FullProductModel(props));
        }

        public T GetProductDetails<T>(string prodId, Func<IDictionary<string, object>, T> productBuilder) where T : FullProductModel
        {
            var url = this.GetProductDetailsUrl(prodId);
            var response = this.ExecuteGet<IDictionary<string, object>>(url);
            this.ValidateResponse(response, HttpStatusCode.OK);

            return productBuilder(response.Content);
        }

        public ServiceResponse GetProductRaw(string prodId)
        {
            var url = this.GetProductDetailsUrl(prodId);
            var response = this.ExecuteGet(url);
            return response;
        }

        public void DeleteProduct(string prodId)
        {
            var url = this.GetProductUrl(prodId);
            var response = this.ExecuteDelete(url);
            this.ValidateResponse(response, HttpStatusCode.Created);
        }

        public string AddReview(string prodId, ProductReviewModel review)
        {
            return this.AddReview(prodId, Serialize(review));
        }

        public string AddReview(string prodId, string reviewJson)
        {
            var url = this.GetReviewsUrl(prodId);
            var response = this.ExecutePost(url, reviewJson);
            this.ValidateResponse(response, HttpStatusCode.Created);
            return response.GetResultId();
        }

        public void RemoveReview(string prodId, string reviewId)
        {
            var url = this.GetReviewUrl(prodId, reviewId);
            var response = this.ExecuteDelete(url);
            this.ValidateResponse(response, HttpStatusCode.Created);
        }

        public ProductReviewModelPagedList GetReviews(string prodId, int pageNumber = 0, int pageSize = 10)
        {
            try
            {
                var url = this.GetReviewsUrl(prodId) + "?pageIndex=" + pageNumber + "&pageSize=" + pageSize;
                var response = this.ExecuteGet<ProductReviewModelPagedList>(url);
                this.ValidateResponse(response, HttpStatusCode.OK);

                return response.Content;
            }
            catch (Exception exc)
            {
                throw new ServiceException("Couldn't get product reviews.", exc);
            }
        }

        public ProductReviewModelPagedList GetReviewsOfAbsentProduct(string prodId, int pageNumber = 0, int pageSize = 10)
        {
            try
            {
                var url = this.GetReviewsUrl(prodId) + "?pageIndex=" + pageNumber + "&pageSize=" + pageSize;
                var response = this.ExecuteGet<ProductReviewModelPagedList>(url);
                this.ValidateResponse(response, HttpStatusCode.NotFound);

                return response.Content;
            }
            catch (Exception exc)
            {
                throw new ServiceException("Couldn't get product reviews.", exc);
            }
        }

        public ProductReviewModelResponse GetReview(string prodId, string reviewId)
        {
            var response = this.GetReviewRaw(prodId, reviewId);
            this.ValidateResponse(response, HttpStatusCode.OK);
            return response.Content;
        }

        public ServiceResponse<ProductReviewModelResponse> GetReviewRaw(string prodId, string reviewId)
        {
            var url = this.GetReviewUrl(prodId, reviewId);
            var response = this.ExecuteGet<ProductReviewModelResponse>(url);
            return response;
        }

        public void UpdateReview(string prodId, string reviewId, ProductReviewModel review)
        {
            this.UpdateReview(prodId, reviewId, Serialize(review));
        }

        public void UpdateReview(string prodId, string reviewId, string reviewJson)
        {
            try
            {
                var url = this.GetReviewUrl(prodId, reviewId);
                var response = this.ExecutePut(url, reviewJson);
                this.ValidateResponse(response, HttpStatusCode.Created);
            }
            catch (Exception exc)
            {
                throw new ServiceException("Couldn't update review.", exc);
            }
        }

        protected string GetProductsUrl()
        {
            return _baseUrl + "api/product/";
        }

        protected string GetProductDetailsUrl(string prodId)
        {
            return this.GetProductUrl(prodId) + "details";
        }

        protected string GetProductUrl(string prodId)
        {
            return this.GetProductsUrl() + prodId + "/";
        }

        protected string GetReviewsUrl(string prodId)
        {
            return this.GetProductUrl(prodId) + "reviews";
        }

        private string GetReviewUrl(string prodId, string reviewId)
        {
            return this.GetReviewsUrl(prodId) + "/" + reviewId;
        }

    }
}
